/*
   +----------------------------------------------------------------------+
   | HipHop for PHP                                                       |
   +----------------------------------------------------------------------+
   | Copyright (c) 2010-2015 Facebook, Inc. (http://www.facebook.com)     |
   +----------------------------------------------------------------------+
   | This source file is subject to version 3.01 of the PHP license,      |
   | that is bundled with this package in the file LICENSE, and is        |
   | available through the world-wide-web at the following url:           |
   | http://www.php.net/license/3_01.txt                                  |
   | If you did not receive a copy of the PHP license and are unable to   |
   | obtain it through the world-wide-web, please send a note to          |
   | license@php.net so we can mail you a copy immediately.               |
   +----------------------------------------------------------------------+
*/

#ifndef incl_HPHP_VM_CODE_GEN_CF_H_
#define incl_HPHP_VM_CODE_GEN_CF_H_

#include "hphp/runtime/vm/jit/vasm-gen.h"
#include "hphp/runtime/vm/jit/vasm-instr.h"
#include "hphp/runtime/vm/jit/vasm-reg.h"

namespace HPHP { namespace jit {

///////////////////////////////////////////////////////////////////////////////

namespace code_gen_detail {

///////////////////////////////////////////////////////////////////////////////

template <class Then>
void ifThen(Vout& vmain, Vout& vcold, ConditionCode cc, Vreg sf,
            Then thenBlock) {
  auto then = vcold.makeBlock();
  auto done = vmain.makeBlock();

  vmain << jcc{cc, sf, {done, then}};

  vcold = then;
  thenBlock(vcold);
  if (!vcold.closed()) vcold << jmp{done};

  vmain = done;
}

template <class Then, class Else>
void ifThenElse(Vout& vmain, Vout& vcold, ConditionCode cc, Vreg sf,
                Then thenBlock, Else elseBlock) {
  auto elze = vmain.makeBlock();
  auto then = vcold.makeBlock();
  auto done = vmain.makeBlock();

  vmain << jcc{cc, sf, {elze, then}};

  vmain = elze;
  elseBlock(vmain);
  if (!vmain.closed()) vmain << jmp{done};

  vcold = then;
  thenBlock(vcold);
  if (!vcold.closed()) vcold << jmp{done};

  vmain = done;
}

template <class Then, class Else>
Vreg cond(Vout& vmain, Vout& vcold, ConditionCode cc, Vreg sf,
          Vreg dst, Then thenBlock, Else elseBlock) {
  auto elze = vmain.makeBlock();
  auto then = vcold.makeBlock();
  auto done = vmain.makeBlock();

  vmain << jcc{cc, sf, {elze, then}};

  vmain = elze;
  auto r1 = elseBlock(vmain);
  vmain << phijmp{done, vmain.makeTuple({r1})};

  vcold = then;
  auto r2 = thenBlock(vcold);
  vcold << phijmp{done, vcold.makeTuple({r2})};

  vmain = done;
  vmain << phidef{vmain.makeTuple({dst})};
  return dst;
}

///////////////////////////////////////////////////////////////////////////////

}

///////////////////////////////////////////////////////////////////////////////
// Conditionals.
//
// Each conditional control-flow helper comes in three flavors:
//    - Emit `thenBlock' to `vmain' (along with all other blocks).
//    - Emit `thenBlock' to `vcold' (with all other blocks in `vmain').
//    - Emit `thenBlock' to `unlikely ? vcold : vmain'.

/*
 * Generate an if-then block construct.
 *
 * Tests `sf' for the branch condition `cc', and jumps to the code generated by
 * the `thenBlock' lambda if the condition holds.  A jmp past the construct is
 * emitted if `thenBlock' is not terminated.
 *
 * `thenBlock' takes a single argument: the Vout to emit to.
 */
template <class Then>
void ifThen(Vout& vmain, ConditionCode cc, Vreg sf, Then thenBlock) {
  code_gen_detail::ifThen(vmain, vmain, cc, sf, thenBlock);
}

template <class Then>
void unlikelyIfThen(Vout& vmain, Vout& vcold, ConditionCode cc, Vreg sf,
                    Then thenBlock) {
  code_gen_detail::ifThen(vmain, vcold, cc, sf, thenBlock);
}

template <class Then>
void ifThen(Vout& vmain, Vout& vcold, ConditionCode cc, Vreg sf,
            Then thenBlock, bool unlikely) {
  code_gen_detail::ifThen(vmain, unlikely ? vcold : vmain,
                          cc, sf, thenBlock);
}

/*
 * Generate an if-then-else block construct.
 *
 * Like ifThen(), except that in addition, we jump to the code generated by
 * `elseBlock' if the condition does not hold.
 *
 * `elseBlock' takes the same arguments as `thenBlock', and we likewise close
 * it with a jmp to past the construct if it is not terminated.
 */
template <class Then, class Else>
void ifThenElse(Vout& vmain, ConditionCode cc, Vreg sf,
                Then thenBlock, Else elseBlock) {
  code_gen_detail::ifThenElse(vmain, vmain, cc, sf, thenBlock, elseBlock);
}

// Nobody currently needs unlikelyIfThenElse().

template <class Then, class Else>
void ifThenElse(Vout& vmain, Vout& vcold, ConditionCode cc, Vreg sf,
                Then thenBlock, Else elseBlock, bool unlikely) {
  code_gen_detail::ifThenElse(vmain, unlikely ? vcold : vmain,
                              cc, sf, thenBlock, elseBlock);
}

/*
 * Generate an if-then-else block construct with a single dst.
 *
 * Like ifThenElse(), except that the blocks are expected to return a dst Vreg.
 * The dsts are phi'd into `dst'.
 *
 * Returns `dst' unaltered, for convenience.
 */
template <class Then, class Else>
Vreg cond(Vout& vmain, ConditionCode cc, Vreg sf,
          Vreg dst, Then thenBlock, Else elseBlock) {
  return code_gen_detail::cond(vmain, vmain, cc, sf, dst, thenBlock, elseBlock);
}

template <class Then, class Else>
Vreg unlikelyCond(Vout& vmain, Vout& vcold, ConditionCode cc, Vreg sf,
                  Vreg dst, Then thenBlock, Else elseBlock) {
  return code_gen_detail::cond(vmain, vcold, cc, sf, dst, thenBlock, elseBlock);
}

template <class Then, class Else>
Vreg cond(Vout& vmain, Vout& vcold, ConditionCode cc, Vreg sf,
          Vreg dst, Then thenBlock, Else elseBlock, bool unlikely) {
  return code_gen_detail::cond(vmain, unlikely ? vcold : vmain,
                               cc, sf, dst, thenBlock, elseBlock);
}

///////////////////////////////////////////////////////////////////////////////

/*
 * Generate a do-while loop.
 *
 * The `regs' list is the list of initial loop registers, which will be phi'd
 * appropriately for the loop.
 *
 * `loopBlock' is the lambda responsible for generating the code.  It takes
 * both the input phidef and output phijcc loop registers as arguments, and
 * should return a single SF Vreg to be tested against `cc'.
 */
template <class Loop>
void doWhile(Vout& v, ConditionCode cc,
             const VregList& regs, Loop loopBlock) {
  auto loop = v.makeBlock();
  auto done = v.makeBlock();

  auto const freshRegs = [&] {
    auto copy = regs;
    for (auto& reg : copy) reg = v.makeReg();
    return copy;
  };
  auto in = freshRegs(), out = freshRegs();

  v << phijmp{loop, v.makeTuple(regs)};

  v = loop;
  v << phidef{v.makeTuple(in)};
  auto const sf = loopBlock(in, out);
  v << phijcc{cc, sf, {done, loop}, v.makeTuple(out)};

  v = done;
  v << phidef{v.makeTuple(freshRegs())};
}

///////////////////////////////////////////////////////////////////////////////

}}

#endif
